/*
 * MidiMatcher.java
 *
 * Created on June 27, 2007, 3:29 AM
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.midi;

import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.MidiMessage;
import tal.drivechain.chain.ChainNode;
import tal.drivechain.wildcard.DoubleElement;
import tal.drivechain.matchers.ValueMatcher;
import tal.drivechain.matchers.VarMatcher;
import tal.drivechain.wildcard.Wildcard;
import tal.drivechain.parsing.Tokenizer;

/**
 *
 * @author Main
 */
public class MidiMatcher extends ValueMatcher {
    public static final int COMMAND_ANY = -1;
    VarMatcher wSysex = null;
    Wildcard wData1 = null;
    Wildcard wData2 = null;
    Wildcard wChannel = null;
    int command;
    
    /** Format:
     *  cmd/[wildcard]/[wildcard]
     *  cmd can be any command name or number or "any"
     */
    protected void parseSpecial(String pattern) throws IllegalArgumentException {
        int cmd;
        
        //divide the rule according to the / chars and determine it's format according to the command type
        Tokenizer ruleTokenizer = new Tokenizer(pattern, '/');
        String[] parts = new String[ruleTokenizer.getTokens().length];
        int i=0;
        while (ruleTokenizer.hasMoreTokens()) {
            parts[i] = ruleTokenizer.nextToken().toLowerCase();
            i++;
        }
        if (parts[0].trim().toLowerCase().equals("any")) cmd=COMMAND_ANY;
        else cmd = MidiUtility.commandNameToCode(parts[0]);
        if (cmd==ShortMessage.NOTE_ON || cmd==ShortMessage.NOTE_OFF) {
            if (parts.length>1 && parts[1]!=null)
                parts[1] = MidiUtility.parseNotes(parts[1]);
        }

        if (cmd==ShortMessage.PITCH_BEND || cmd==ShortMessage.CHANNEL_PRESSURE || cmd==ShortMessage.PROGRAM_CHANGE) {
            // needs to do a special job for pitchbend...
            if (parts.length>1) {
                wData1 = Wildcard.parseDefinition(parts[1], new DoubleElement());
            } else wData1 = new Wildcard(null, null, Wildcard.Operator.ANY);
            if (parts.length>2) {
                wChannel = Wildcard.parseDefinition(parts[2], new DoubleElement());
            } else wChannel = new Wildcard(null, null, Wildcard.Operator.ANY);
        } else if (cmd==SysexMessage.SYSTEM_EXCLUSIVE) {
            //sysex stuff here
        } else {
            if (parts.length>1) {
                wData1 = Wildcard.parseDefinition(parts[1], new DoubleElement());
            } else wData1 = new Wildcard(null, null, Wildcard.Operator.ANY);
            if (parts.length>2) {
                wData2 = Wildcard.parseDefinition(parts[2], new DoubleElement());
            } else wData2 = new Wildcard(null, null, Wildcard.Operator.ANY);
            if (parts.length>3) {
                wChannel = Wildcard.parseDefinition(parts[3], new DoubleElement());
            } else wChannel = new Wildcard(null, null, Wildcard.Operator.ANY);
        }
        this.command = cmd;
    }
    
    public boolean matchSpecial(Object value, ChainNode context) {
        MidiMessage message = (MidiMessage)value;
        if (command==SysexMessage.SYSTEM_EXCLUSIVE || command==SysexMessage.SPECIAL_SYSTEM_EXCLUSIVE) {
            if (command!=message.getStatus()) return false;
            SysexMessage sysexmsg = (SysexMessage)message;
            //if (!wSysex.matches(the sysex var)) return false
            return false;
        } if (command>=0xF1 && command<=0xFF) { // system-realtime or system-common
            if (command!=message.getStatus()) return false;
            ShortMessage smsg = (ShortMessage)message;
            if (!wData1.matches(smsg.getData1(), context)) return false;
            if (!wData2.matches(smsg.getData2(), context)) return false;
            return true;
        } else {
            ShortMessage smsg = (ShortMessage)message;
            if (command!=smsg.getCommand() && command!=COMMAND_ANY) return false;
            if (!wData1.matches(smsg.getData1(), context)) return false;
            if (!wData2.matches(smsg.getData2(), context)) return false;
            if (!wChannel.matches(smsg.getChannel(), context)) return false;
            return true;
        }
    }
    
    @Override
    public String toString() {
        return MidiUtility.commandCodeToString(command) + "/" + wData1.toString() + "/" + wData2.toString() + "/" + wChannel.toString();
    }
}
